from forest.resource import Resources, Resource, ValidationError
from forest.resources.common import translate_disk_name

import copy

class PartitionTable(Resource):
    ''' Creates a partition table on the specified disk.

        A simple single disk partitioning example::

            resources:
            - $: partition-table
              disk: sda
              partitions:
              - name: bios-boot
                size: 1
                flags:
                  bios_grub: True

              - name: boot
                size: 500

              - name: swap
                size: 1000

              - name: root
                size: grow

        .. describe:: disk

            Required. a disk name.

        .. describe:: partitions

            Required. A list of partitions.

        Each entry in partitions list is a partitions to apply to the disk:

        .. describe:: partitions[*].size

            Required. integer megabytes or grow to grow to the rest of the disk

        .. describe:: partitions[*].name

            Optional. partition name(gpt)

        .. describe:: partitions[*].type

            Optional. partition type (msdos label, extended or primary)

        .. describe:: partitions[*].flags

            Optional. a dictionary of boolean flags to set or unset (gpt)

            Available flags:

            * bios_grub: marks the partition as bios_grub. needed for gpt/efi
            * raid: marks the partition as raid

        .. describe:: format

            Optional. the partition table format msdos or gpt (default: gpt).
    '''
    __register__ = "partition-table"

    def get_disk_specs(self):
        diskid = self.get_device()
        specs = []
        #for diskspec in self.value.get('partitions'):
#            if isinstance(diskid, list):
#                for d in diskid:
#                    copy = diskspec.copy()
#                    copy['disk'] = d
#                    self.append((DiskSpec(config, copy)))
#                continue
#
        if not (isinstance(diskid, basestring) or isinstance(diskid, list)):
            raise Exception("Disk name %s invalid. Should be a string diskname or list of string disknames." % diskid)

        disks = diskid
        if isinstance(disks, basestring):
            disks = [diskid]

        for disk in disks:
            a_copy = copy.deepcopy(self.value)
            a_copy['device'] = disk
            specs.append(DiskSpec(a_copy))
        return specs

    def get_device(self):
        disk = self.value.get('device')

        # try to find the disk using disk-name mapping resources
        return translate_disk_name(self.config, disk)

    def get_id(self):
        return 'partition-table:' + str(self.value.get('device'))


class DiskSpec(object):
    def __init__(self, definition):
        self.disk = definition.get('device')
        self.partitions = []
        for part in definition.get('partitions'):
            self.partitions.append(PartitionSpec(part))

    def get_id(self):
        return 'diskspec:' + self.disk

    def get_disk(self):
        return self.disk

    def get_partitions(self):
        return self.partitions

    def __repr__(self):
        return 'Disk(name=%s, partitions=[\n%s]\n)' % (self.disk, '\n'.join(repr(p) for p in self.partitions))

class PartitionSpec(object):
    ''' gpt-only for now
    '''
    def __init__(self, definition):
        self.value = {}
        valid_keys = ['name', 'type', 'size', 'fstype', 'flags']

        for k in definition.iterkeys():
            if k in valid_keys:
                continue
            raise ValidationError('Undefined partition option {}'.format(k))

        necessary_keys = ['name', 'size']

        for necessary in necessary_keys:
            if necessary in definition.iterkeys():
                continue
            raise ValidationError('Missing partition option {}. Please set it.'.format(necessary))

        self.value['name'] = definition.get('name')
        self.value['type'] = definition.get('type', 'primary')
        self.value['size'] = definition.get('size','')
        self.value['fs'] = definition.get('fstype', 'non-fs')
        self.value['flags'] = definition.get('flags', {})


        flags = ['bios_grub', 'legacy_boot']

        err = False
        for k in self.value.get('flags').iterkeys():
            if k in flags:
                continue
            err = True
        if err:
            raise ValidationError("Only the following flags are supported: {}".format(flags))

        size = ['grow']

        if str(self.value['size']).lower() != 'grow':
            try:
                self.value['size'] = int(self.value['size'])
            except ValueError:
                raise ValidationError("Partition size must be either 'grow' or the number of MB")

        types = ['primary']

        if self.value.get('type').lower() not in types:
            raise ValidationError("Partition type if set must be {}".format(types))

    def get_flags(self):
        return  self.value['flags']

    def get_name(self):
        return  self.value['name']

    def get_type(self):
        return  self.value['type']

    def get_fs(self):
        return  self.value['fs']

    def get_size(self):
        return  self.value['size']


    def __repr__(self):
        return 'Partition(name=%s, type=[%s], size=%s, fs=%s, flags=%s)' % (self.name, self.type, self.size, self.fs, self.flags)
